package com.ruoyi.framework.aspectj;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.ClassPool;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author mwj
 * 日志打印类
 */
@Aspect
@Component
@Slf4j
public class LogRecordAspect {
    private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    protected static boolean locateMethodLineNumber = true;
    private static final ClassPool pool = ClassPool.getDefault();

    @Pointcut("execution(* com.ruoyi.*.controller.*Controller.*(..)) || execution(* com.ruoyi.*.controller.*.*Controller.*(..))")
    public void executionService() {
    }

    //2024-09-11 修改 去除try cache  ,直接抛出异常 让框架统一异常处理类GlobalExceptionHandler  来捕获
    @Around("executionService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (Objects.isNull(sra)) {
            log.error("请求异常");
            return null;
        }
        HttpServletRequest request = sra.getRequest();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String paraString = JSON.toJSONString(request.getParameterMap());
        // 读取JSON请求体
        String jsonBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
        // 使用 Fastjson 的 TypeReference 来解析 JSON 字符串
        Map<String, String> paramMap = null;
        try {
            paramMap = JSON.parseObject(jsonBody, new TypeReference<Map<String, String>>() {});
        }catch (Exception e){
            paramMap = new HashMap<>();
        }
        Integer line = getLineNumber(pjp.getSignature().getName(), pjp.getTarget().getClass().getName());
        //南风怡人
        StringBuilder buffer = new StringBuilder("\nBXOLINE-- request report ---------")
                .append((sdf.get()).format(new Date())).append(" --------------------------\n");
        buffer.append("Url         : ").append(method).append("  ").append(uri).append("\n");
        buffer.append("Controller  : ").append(pjp.getSignature().getDeclaringTypeName())
                .append(".(").append(pjp.getTarget().getClass().getSimpleName()).append(".java:").append(line).append(")\n");
        buffer.append("Method      : ").append(pjp.getSignature().getName()).append("\n");
        if(StringUtils.isNotEmpty(paraString) && !"{}".equals(paraString)){
            buffer.append("UrlPara     : ").append(paraString).append("\n");
        }else{
            buffer.append("UrlPara     : ").append(paramMap).append("\n");
        }

        String format = String.format("\033[31m%s\033[0m", buffer);
        log.info(format);
        // result的值就是被拦截方法的返回值
        Object result = pjp.proceed();
        String sbResponse = "返回值: " +
                pjp.getTarget().getClass().getSimpleName() + " --- " +
                pjp.getSignature().getName() + ": " +
                JSON.toJSONString(result);
        String after = String.format("\033[31m%s\033[0m", sbResponse);
        log.info(after);
        log.info("\n\n\n\n\n");
        return result;
    }

    private int getLineNumber(String methodName, String className){
        int lineNumber = 1;
        if (locateMethodLineNumber) {
            try {
                lineNumber = pool.get(className).getDeclaredMethod(methodName).getMethodInfo().getLineNumber(0);
            } catch (Exception e) {
                return 0;
            }
        }
        return lineNumber;
    }

}